#include <iostream>
#include <queue>
#include <cmath>
using namespace std;
#define DEBUG
inline int read()
{
    int c=getchar(), f=1, x=0;
    if(c=='-') f*=-1, c=getchar();
    while(c<'0'&&'9'<c) c=getchar();
    while('0'<=c&&c<='9') 
        x=(x<<3)+(x<<1)+c-'0', c=getchar();
    return f*x;
}

inline void write(int x)
{
    if(x<0) putchar('-'), x*=-1;
    if(x>=10) write(x/10);
    putchar(x%10+'0');
}
const int N=1e5+5;
const int M=350+5;
const int MOD=998244353;
int n, m;

struct matrix {
    int v[2][2];
    matrix(bool isE=false) { //默认为E,不影响前缀乘积
        v[0][0]=v[1][1]=isE;
        v[1][0]=v[0][1]=0;
    }
    friend matrix operator*(const matrix& a, const matrix& b) {
        matrix ans;
        ans.v[0][0]=(1ll*a.v[0][0]*b.v[0][0]+1ll*a.v[0][1]*b.v[1][0]) % MOD;
        ans.v[0][1]=(1ll*a.v[0][0]*b.v[0][1]+1ll*a.v[0][1]*b.v[1][1]) % MOD;
        ans.v[1][0]=(1ll*a.v[1][0]*b.v[0][0]+1ll*a.v[1][1]*b.v[1][0]) % MOD;
        ans.v[1][1]=(1ll*a.v[1][0]*b.v[0][1]+1ll*a.v[1][1]*b.v[1][1]) % MOD;
        return ans;
    }
    friend istream& operator >> (istream& in, matrix& x) {
        return in >> x.v[0][0] >> x.v[0][1] >> x.v[1][0] >> x.v[1][1];
    }
    friend ostream& operator << (ostream& out, const matrix& x) {
        for (int i=0; i<2; i++)
            for (int j=0; j<2; j++)
                out << x.v[i][j] << " ";
        return out;
    }
};

struct node {
    int op;
    matrix l, r;
    friend istream& operator >> (istream& in, node& nd) 
    {
        in >> nd.op;
        if (nd.op == 1) in >> nd.l, nd.r=matrix(true); //插入到头部
        else if (nd.op == 2) in >> nd.r, nd.l=matrix(true); //插入到尾部
        else nd.l=nd.r=matrix(true); //删除插入最晚的
        return in;
    }
} op[N];

int len; //分块长度
struct block {
    int l, r, neg, sz; //左右边界 op3 有效操作量
    matrix suml[M], sumr[M]; //从左到右 从右到左的前缀积

    void build(int idx) 
    {
        l=idx*len, r=min(l+len-1, n-1), neg=0, sz=0;
        deque<int> st;
        for (int i=l; i<=r; i++) {
            if (op[i].op!=3) st.push_back(i);
            else if (st.empty()) neg++; //多余的撤销动作
            else st.pop_back(); //删除
        }
        sz=st.size();
        suml[0]=sumr[0]=matrix(true);
        for (int i=0; i<st.size(); i++) {
            suml[i+1]=op[st[i]].l*suml[i];
            sumr[i+1]=sumr[i]*op[st[i]].r;
        }
    }
} blk[M];

matrix query(int l, int r) {
    int blkl=l/len, blkr=r/len; //分块编号
    if (blkl == blkr) { // 在一个分块内
        deque<int> st;
        for (int i=l; i<=r; i++)
            if (op[i].op!=3) st.push_back(i);
            else if (!st.empty()) st.pop_back(); //删除
        matrix L=matrix(true), R=matrix(true);
        for (int i=0; i<st.size(); i++) 
        {
            L=op[st[i]].l*L;
            R=R*op[st[i]].r;
        }
        return L*R;
    }
    else //右边界所在分块 中间分块 左边界所在分块
    {
        int neg=0;
        deque<int> st;
        for (int i=blk[blkr].l; i<=r; i++) //查询区间的所有操作
        {
            if (op[i].op!=3) st.push_back(i);
            else if (st.empty()) neg++; //删除操作
            else st.pop_back();
        }
        matrix L=matrix(true), R=matrix(true);
        for (int i=0; i<st.size(); i++) //右边界分块
        {
            L=op[st[i]].l*L;
            R=R*op[st[i]].r;
        }

        for (int i=blkr-1; i>=blkl+1; i--) //右->左的中间分块
        {
            if (blk[i].sz<=neg) neg=neg-blk[i].sz+blk[i].neg; //全删了
            else 
            {
                L=L*blk[i].suml[blk[i].sz-neg];
                R=blk[i].sumr[blk[i].sz-neg]*R;
                neg=blk[i].neg;
            }
        }
        while (!st.empty()) st.pop_back();
        for (int i=l; i<=blk[blkl].r; i++) 
        {
            if (op[i].op!=3) st.push_back(i);
            else if (!st.empty()) st.pop_back();
        }
        while (neg && !st.empty()) neg--, st.pop_back(); //之前记录的多余删除操作
        for (int i=st.size()-1; i>=0; i--) 
        {
            L=L*op[st[i]].l;
            R=op[st[i]].r*R;
        }
        return L*R;
    }
}

void init()
{
    cin >> n >> m;
    len=max(1, (int)sqrt(n)); //分块长度

    for (int i=0; i<n; i++) cin >> op[i];
    for (int i=0; i<n; i += len) blk[i/len].build(i/len);
    
}

void solve()
{
    init();
    while (m--) {
        int v, idx, l, r;
        cin >> v;
        if (v == 1) {
            cin >> idx >> op[--idx];
            blk[idx/len].build(idx/len);
        }
        else { //查询
            cin >> l >> r;
            cout << query(--l, --r) << endl;
        }
    }  
}

signed main()
{
    #ifdef DEBUG
        freopen("../in.txt", "r", stdin);
        freopen("../out.txt", "w", stdout);
    #endif

    int T=1; //cin >> T; 
    while(T--) 
    {
        solve();
    }
    return 0;
}